#include "FileStream.h"
#define _FILE_OFFSET_BITS 64
namespace zl
{
	namespace stream
	{
		FileStream::FileStream(const std::wstring& filename, AccessRight _accessRight) :accessRight(_accessRight)
		{
			std::string mode;
			switch (accessRight)
			{
			case zl::stream::FileStream::ReadOnly:
				mode = "rb";
				break;
			case zl::stream::FileStream::WriteOnly:
				mode = "wb";
				break;
			case zl::stream::FileStream::ReadWrite:
				mode = "a+b";
				break;
			default:
				break;
			}
#ifdef _MSC_VER
			if (fopen_s(&file, wtoa(filename).c_str(), mode.c_str()) != 0)
			{
				file=0;
			}
#else
			file = fopen(wtoa(filename).c_str(), mode.c_str());
#endif
		}

		FileStream::~FileStream()
		{
			Close();
		}

		bool FileStream::CanRead()const
		{
			return file != 0 && (accessRight == ReadOnly || accessRight == ReadWrite);
		}

		bool FileStream::CanWrite()const
		{
			return file != 0 && (accessRight == WriteOnly || accessRight == ReadWrite);
		}

		bool FileStream::CanSeek()const
		{
			return file != 0;
		}

		bool FileStream::CanPeek()const
		{
			return file != 0 && (accessRight == ReadOnly || accessRight == ReadWrite);
		}

		bool FileStream::IsLimited()const
		{
			return file != 0 && accessRight == ReadOnly;
		}

		bool FileStream::IsAvailable()const
		{
			return file != 0;
		}

		void FileStream::Close()
		{
			if (file != 0)
			{
				fclose(file);
				file = 0;
			}
		}

		pos_t FileStream::Position()const
		{
			if (file != 0)
			{
#ifdef _MSC_VER
				fpos_t position = 0;
				if (fgetpos(file, &position) == 0)
				{
					return position;
				}
#else
				return (pos_t)ftello(file);
#endif
			}
			return -1;
		}

		pos_t FileStream::Size()const
		{
			if (file != 0)
			{
#ifdef _MSC_VER
				fpos_t position;
				if (fgetpos(file, &position) == 0)
				{
					if (fseek(file, 0, SEEK_END) == 0)
					{
						pos_t size = Position();
						if (fsetpos(file, &position) == 0)
						{
							return size;
						}
					}
				}
#else
				long long position = ftello(file);
				fseeko(file, 0, SEEK_END);
				long long size = ftello(file);
				fseeko(file, position, SEEK_SET);
				return (pos_t)size;
#endif
			}
			return -1;
		}

		void FileStream::Seek(pos_t _size)
		{
			if (Position() + _size>Size())
			{
#ifdef _MSC_VER
				_fseeki64(file, 0, SEEK_END);
#else
				fseeko(file, 0, SEEK_END);
#endif
			}
			else if (Position() + _size<0)
			{
#ifdef _MSC_VER
				_fseeki64(file, 0, SEEK_SET);
#else
				fseeko(file,0,SEEK_SET);
#endif
			}
			else
			{
#ifdef _MSC_VER
				_fseeki64(file, _size, SEEK_CUR);
#else
				fseeko(file,0,SEEK_CUR);
#endif
			}
		}

		void FileStream::SeekFromBegin(pos_t _size)
		{
			if (_size>Size())
			{
#ifdef _MSC_VER
				_fseeki64(file, 0, SEEK_END);
#else
				fseeko(file,0,SEEK_END);
#endif
			}
			else if (_size<0)
			{
#ifdef _MSC_VER
				_fseeki64(file, 0, SEEK_SET);
#else
				fseeko(file,0,SEEK_SET);
#endif
			}
			else
			{
#ifdef _MSC_VER
				_fseeki64(file, _size, SEEK_SET);
#else
				fseeko(file,_size,SEEK_SET);
#endif
			}
		}

		void FileStream::SeekFromEnd(pos_t _size)
		{
			if (_size<0)
			{
#ifdef _MSC_VER
				_fseeki64(file, 0, SEEK_END);
#else
				fseeko(file,0,SEEK_END);
#endif
			}
			else if (_size>Size())
			{
#ifdef _MSC_VER
				_fseeki64(file, 0, SEEK_SET);
#else
				fseeko(file,0,SEEK_SET);
#endif
			}
			else
			{
#ifdef _MSC_VER
				_fseeki64(file, -_size, SEEK_END);
#else
				fseeko(file,0,SEEK_END);
#endif
			}
		}

		size_t FileStream::Read(void* _buffer, zint _size)
		{
			assert(file != 0);
			assert(_size >= 0);
			return fread(_buffer, 1, (size_t)_size, file);
		}

		size_t FileStream::Write(void* _buffer, zint _size)
		{
			assert(file != 0);
			assert(_size >= 0);
			return fwrite(_buffer, 1, (size_t)_size, file);
		}

		size_t FileStream::Peek(void* _buffer, zint _size)
		{
			assert(file != 0);
			assert(_size >= 0);
#ifdef _MSC_VER
			fpos_t  position;
			if (fgetpos(file, &position) == 0)
			{
				size_t count = fread(_buffer, 1, (size_t)_size, file);
				if (fsetpos(file, &position) == 0)
				{
					return count;
				}
			}
			return -1;
#else
			long long position = ftello(file);
			size_t count = fread(_buffer, 1, (size_t)_size, file);
			fseeko(file, position, SEEK_SET);
			return count;
#endif
		}

	};
};
